Skip to content

[Refactor/#118] 뒤로가기 딥링크 폴백을 useBackNavigation 훅으로 통합 - #119

Open
xeoxxn wants to merge 3 commits into
mainfrom
refactor/#118-back-navigation
Open

xeoxxn wants to merge 3 commits into
mainfrom
refactor/#118-back-navigation

Conversation

@xeoxxn

@xeoxxn xeoxxn commented Oct 14, 2026

Copy link
Copy Markdown
Collaborator

#️⃣연관된 이슈

🎯 해결하려는 문제가 무엇인가요?

화면으로 바로 들어오면(딥링크·앱 WebView 진입·주소 직접 입력) 뒤에 갈 화면이 없어서 헤더 뒤로가기가 아무 일도 하지 않는다. 사용자 입장에서는 버튼이 먹통이고, 모달을 띄워 둔 화면이면 그대로 갇힌다.

이미 5개 화면이 각자 막아 두고 있었는데, 5줄짜리 블록이 토씨 하나 다르지 않게 복붙돼 있었다.

const historyIndex = (window.history.state as { idx?: number } | null)?.idx;
if (historyIndex !== undefined && historyIndex > 0) {
  navigate(-1);
  return;
}
navigate("/", { replace: true });   // ← 이 줄의 갈 곳만 화면마다 다름

window.history.state를 직접 캐스팅해 읽는 코드라 react-router를 올릴 때 깨질 수 있는 자리인데, 그게 5곳에 흩어져 있었다. ScreenLayoutRoute도 전환 방향을 정하려고 같은 값을 따로 읽고 있어서 실제로는 6곳이었다.

⭐ 어떻게 해결했나요?

커밋을 세 개로 나눴고, 각각 독립적으로 타입체크를 통과한다.

1. history.state.idx 읽는 코드를 한곳으로 (094c3b9)

lib/historyIndex.ts의 getHistoryIndex() 하나만 읽는다. ScreenLayoutRoute에 있던 같은 함수를 여기로 옮겼다. 이제 저장소에서 history.state.idx를 건드리는 곳은 이 파일뿐이다.

2. 뒤로가기 폴백을 훅으로 (bc42a92)

const goBack = useBackNavigation("/events");

src/hooks/useBackNavigation.ts. 앱 안에서 타고 들어왔으면 navigate(-1), 바로 들어왔으면 fallbackPath로 replace한다. 되돌아올 화면이 아니라 대신 가는 곳이라 히스토리를 치환한다.

화면 fallbackPath
SearchScreen /
NotificationsScreen /
NotificationsSettingsScreen NOTIFICATIONS_PATH
EventsApplicationScreen /events
FeedbacksNewScreen /feedbacks

검색·알림 설정은 이 블록이 navigate를 쓰는 유일한 자리여서 선언과 import도 같이 지웠다.

3. 컨벤션 문서 (3a70c38) — coding-style.md 라우팅 절에 훅 사용 규칙을 넣었다. 새 화면에서 navigate(-1)만 쓰면 딥링크로 열렸을 때 먹통이 된다는 점을 같이 적었다.

🧩 이 PR의 한계 & 트레이드오프

  • 설정 화면 3개([Feat/#96] 설정 화면과 전화번호 변경·회원 탈퇴 화면 구현 #97)는 이 PR에 없다. SettingsScreen·PhoneChangeScreen·WithdrawalScreen은 navigate(-1)만 있어서 같은 문제가 있는데, 아직 main에 없어서([Feat/#96] 설정 화면과 전화번호 변경·회원 탈퇴 화면 구현 #97 리뷰 중) 이 브랜치에서 건드릴 수 없다. #97이 먼저 머지되면 이 브랜치에 main을 머지하고 세 화면도 같이 넣겠다. 그 뒤에 머지되면 [Feat/#96] 설정 화면과 전화번호 변경·회원 탈퇴 화면 구현 #97 쪽에서 한 줄씩 붙이면 된다.
  • 뒤로가기 헤더 마크업은 묶지 않았다. TopNavigationButton + IconChevronLeft 조합이 15개 파일에 있지만 5줄짜리 JSX고, 화면마다 title·trailing 구성이 제각각이라(설정은 타이틀 있음, 탈퇴는 없음, 알림은 trailing에 설정 버튼) 훅 하나로 받으면 prop이 늘어 지금보다 읽기 어려워진다. 중복의 핵심은 마크업이 아니라 위의 폴백 로직이었다.
  • fallbackPath는 화면마다 사람이 정한다. "한 단계 위"를 자동으로 알 방법이 없어서 호출부가 넘긴다. 기존 5개는 원래 값을 그대로 옮겼다.

⛓️ 기존 기능에 미치는 영향

  • 5개 화면의 동작은 그대로다. 같은 조건에서 같은 곳으로 간다 — 로직을 옮기기만 했다.
  • ScreenLayoutRoute의 전환 방향 판정도 그대로다. 읽는 함수만 바뀌었고 narrowing 방식은 원래 ScreenLayoutRoute 쪽 구현(typeof·in 검사)을 택했다 — 화면들이 쓰던 as 캐스팅보다 안전하다.

🔀 Edge Case & 실패 시나리오

실제 클릭으로 확인했다.

상황 결과
/notifications/settings 직접 진입(idx=0) → 뒤로가기 /notifications
/search 직접 진입 → 뒤로가기 /
/feedbacks/new 직접 진입 → 뒤로가기 /feedbacks
/notifications → 설정(idx=1) → 뒤로가기 /notifications(idx=0), 즉 navigate(-1) 경로
/events/1/apply 직접 진입 → 작성 전 뒤로가기 모달 없이 /events
/events/1/apply 직접 진입 → 작성 후 뒤로가기 → "나가기" 모달 뜬 뒤 /events
  • history.state에 idx가 없는 환경(라우터가 아직 안 쓴 경우)은 null로 보고 폴백으로 간다 — 먹통이 되는 것보다 낫다.

💬 리뷰 포인트

  • [c] 각 화면의 fallbackPath가 맞는지 봐주세요. 기존 값을 그대로 옮긴 거라 원래 선택이 맞았는지는 따로 보지 않았습니다. 특히 검색·알림이 /인 게 맞는지(진입점이 여러 화면의 헤더라 어디로 보내야 할지 애매합니다).
  • [c] 훅을 src/hooks/에 뒀습니다. usePrefersReducedMotion과 같은 자리인데, 라우팅에 묶인 훅이라 app/ 쪽이 낫다고 보시면 알려주세요.
  • [a] 설정 화면 3개는 [Feat/#96] 설정 화면과 전화번호 변경·회원 탈퇴 화면 구현 #97 머지 순서에 따라 이 PR에 추가하겠습니다.

react-router가 내부적으로 정한 형태라 라이브러리를 올릴 때 깨질 수 있는 자리인데,
ScreenLayoutRoute(전환 방향 판정)와 여러 화면의 뒤로가기가 각자 읽고 있었다.
lib/historyIndex.ts 하나만 읽도록 하고 ScreenLayoutRoute를 먼저 옮긴다.
화면으로 바로 들어오면(딥링크·앱 WebView 진입·주소 직접 입력) 뒤에 갈 화면이 없어서
navigate(-1)이 아무 일도 하지 않는다. 이걸 막는 5줄짜리 블록이 5개 화면에 토씨 하나
다르지 않게 복붙돼 있었고, 다른 건 마지막 줄의 갈 곳뿐이었다.

useBackNavigation(fallbackPath) 하나로 묶는다. 동작은 그대로다.

- 검색 → "/", 알림 → "/", 알림 설정 → 알림 목록
- 행사 신청 → "/events", 열린피드백 작성 → "/feedbacks"

검색·알림 설정은 이 블록이 navigate를 쓰는 유일한 자리여서 선언과 import도 같이 지웠다.

뒤로가기 헤더 마크업(TopNavigationButton + IconChevronLeft)은 묶지 않았다. 15개 파일에
있지만 5줄짜리 JSX고, 화면마다 title·trailing 구성이 달라 훅으로 받으면 prop만 늘어난다.
@coderabbitai

coderabbitai Bot commented Oct 14, 2026

Copy link
Copy Markdown

Important

  • 🔍 Trigger review

This repository does not receive automatic reviews because it has fewer than 10 stars.

⚙️ Run configuration
  • Configuration used: defaults
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: f78344d7-8a70-42b4-a2ba-c3e2ccc25b83
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

뒤로가기 딥링크 폴백을 useBackNavigation 훅으로 통합

1 participant